/**
 * 触摸板
 * 组件:多指触摸板,四个鼠标按键,右下角设置按钮
 * 功能说明: 详见设置弹窗
 * 触摸板代码逻辑: 详见注释
 *
 * 自义定组件
 * https://blog.csdn.net/qq_39109182/article/details/127821485
 *
 * 需要样式
 * <link rel="stylesheet" href="../component/SimpleDialog.css">
 *
 * @author hugan
 * @date 2023/10/6
 */
import "../common/plugins/vue/2.6.11.min.js"
import "../common/plugins/hammerjs/2.0.8.min.js"
import storageUtil from "../common/util/storageUtil.js";

let component = {
  template: `<div>
<!--
触摸板按钮 
"touchpadButton"的样式由外部决定
-->
<div style="position: relative;">
  <div class="touchpadButton" style="display:inline-grid; position: absolute; top: 0; left: 0;" v-show="showFB">
    <button keyCode="32768" isMouse="true">前进</button>
    <button keyCode="16384" isMouse="true">后退</button>
  </div>
  <div class="touchpadButton" style="display:flex; position: absolute; top: 0; right: 0;" v-show="showMR">
    <button keyCode="2048" isMouse="true">中键</button>
    <button keyCode="4096" isMouse="true">右键</button>
  </div>
</div>
<!--多指触摸板-->
<div ref="touchpad" style="height: 99%; background-color: #c9d2dc; border-radius: 5px; border: 2px solid darkseagreen;
        display: flex; justify-content: flex-end; align-items: flex-end;">
  <!--设置按钮-->
  <button class="btnShowSetting" style="width: 38px; height: 38px;" @click="showSettingDialog=true">❖</button>
</div>

<!--设置弹窗-->
<div class="simple-dialog" v-show="showSettingDialog">
  <div class="simple-dialog-bg" @click="showSettingDialog=false"></div>
  <div class="simple-dialog-content">
    <div style="display: flex; align-items: center; height: 38px; margin-top: 10px;">
        <span style="width: 120px; white-space: nowrap;" v-text="'灵敏度:'+(touchRatio/10).toFixed(1)"></span>
        <input type="range" min="1" max="200" style="height: 100%;width: 100%; outline: none; cursor: pointer; "
            v-model="touchRatio">
    </div>
    <div style="display: grid; grid-template-columns: repeat(2, 1fr); padding: 10px 0;">
        <label>[前进,后退]<input type="checkbox" v-model="showFB"></label>
        <label>[中键,右键]<input type="checkbox" v-model="showMR"></label>
    </div>
    <div>
      <span>多指操作说明:</span> 
      <div style="margin-left: 20px">
        双指点击:[右键]<br>
        双指滑动:[横向和纵向滚动]<br>
        三指点击:[中键]<br>
        三指左右滑动:[后退,前进]<br>
      </div>
    </div>
  </div>
</div>

<!--debug日志-->
<div style="position: absolute; top: 50%; left: 0; width: 300px" v-if="debug">
  <textarea rows="10" style="width: 100%;font-size: 8px" readonly v-text="debugText"></textarea>
  <button style="position: absolute; top: 5%; right: 0;" @click="debugText=''">清空</button>
</div>

</div>`,
  props: {
    onTouchpadEvent: {
      type: Function,
      default: function (type, data) {
        console.log('缺省onTouchpadEvent', type, data)
      }
    },
    onScrollEvent: {
      type: Function,
      default: function (data) {
        console.log('缺省onScrollEvent', data)
      }
    },
    /**
     * [touchUtil.js]
     * 组件被旋转的角度[transform: rotate("result"deg);]
     * return result,如'90,-90'
     */
    getRotateDegree: {
      type: Function,
      default: function (data) {
        return 0
      }
    },
  },
  data() {
    return {
      //触摸板[移动,滚动条]灵敏度:[1,n]=>[1/10,n/10]倍
      touchRatio: 38,
      //显示[中键,右键]按钮
      showMR: false,
      //显示[前进,后退]按钮
      showFB: false,
      //
      showSettingDialog: false,
      //用于移动端上显示日志
      // debug: true,
      debug: false,
      debugText: '',
    }
  },
  watch: {
    touchRatio(newValue) {
      storageUtil.setStore('touch.ratio', newValue)
    },
    showMR(newValue) {
      storageUtil.setStore('touch.showMR', newValue)
    },
    showFB(newValue) {
      storageUtil.setStore('touch.showFB', newValue)
    },
  },
  mounted() {
    // console.log('mounted')
    this.reloadConfig()
    this.initTouchpad()
  },
  methods: {
    /**
     * 初始化[触摸板]
     */
    initTouchpad() {
      let touchpad = this.$refs['touchpad']
      let hammer = new Hammer(touchpad);
      // 配置 https://hammerjs.github.io/recognizer-tap/
      hammer.get('tap').set({//点击配置
        threshold: 1000,// 过小时,会丢失很多点击事件,While doing a tap some small movement is allowed.
        posThreshold: 200,// The maximum position difference between multiple taps.
      });
      hammer.get('pan').set({//移动配置
        pointers: 0,//Required pointers. 0 for all pointers.
        threshold: 1,//Minimal pan distance required before recognizing.
        direction: Hammer.DIRECTION_ALL,//不设置时,会经常丢失事件
      });
      hammer.get('press').set({time: 0});//按下配置

      //ws业务方法
      hammer.doMouseMoveStart = e => {
        this.addLog(`doMouseMoveStart.${e.type}`)
        let x = e.center.x
        let y = e.center.y
        this.onTouchpadEvent('down', {action: 1, x, y})
      }
      hammer.doMouseMove = e => {
        let x = e.center.x
        let y = e.center.y
        // this.addLog(`doMouseMove.${e.type}, [${x}, ${y}]`)
        let ratio = this.touchRatio / 10
        this.onTouchpadEvent('move', {action: 2, x, y, ratio})
      }
      hammer.doMousePress = e => {
        this.addLog(`doMousePress.${e.type}`)
        this.onTouchpadEvent('down', {action: 4})
      }
      hammer.doMouseUp = e => {
        this.addLog(`doMouseUp.${e.type}`)
        this.onTouchpadEvent('up', {action: 5})
      }
      hammer.doMouseButton = (e, keyCode, keyName) => {
        this.addLog(`doMouseButton.${e.type}, keyCode=${keyCode}, keyName=${keyName}`)
        //只有[点击], 没有[按下/长按/抬起]的事件; 因为hammerjs没回调
        this.onTouchpadEvent('mouseButton', {action: 3, type: 2, keyCode})
      }
      hammer.doScroll = (e, isVertical, scrollValue) => {
        let valueKey = isVertical ? 'scrollLastValueY' : 'scrollLastValueX';
        if (hammer[valueKey] !== scrollValue) {
          this.addLog(`scroll: ${isVertical}, ${hammer[valueKey]} -> ${scrollValue}`)
          let isAdd = scrollValue > hammer[valueKey]
          this.onScrollEvent({isVertical, isAdd})
          hammer[valueKey] = scrollValue
        }
      };
      //其他逻辑方法
      hammer.doReset = e => {
        // this.addLog(`doReset.${e.type}`)
        if (hammer.mousePressing) {
          hammer.mousePressing = false
          hammer.doMouseUp(e)
        }
        hammer.scrollLastValueX = 0;//水平滚动条
        hammer.scrollLastValueY = 0;//竖直滚动条
      }
      /**
       * 处理组件旋转
       * @param fixCenter 修正[事件坐标] e.center.XY
       * @param fixDelta 修正[delta坐标] e.deltaXY
       */
      hammer.fixRotate = (e, fixCenter, fixDelta) => {
        let degree = this.getRotateDegree()
        if (degree === 0) {
          //无特殊处理
        } else if (degree === 90) {
          if (fixCenter) {
            //注意: 这算法'是不准确的',只修正了[轴和方向],[值]是不对的,但对当前功能无影响
            let temp = e.center.x
            e.center.x = e.center.y + 0
            e.center.y = -temp
          }
          if (fixDelta) {
            let temp = e.deltaX
            e.deltaX = e.deltaY + 0
            e.deltaY = -temp
          }
        } else if (degree === -90) {
          if (fixCenter) {
            let temp = e.center.x
            e.center.x = -e.center.y
            e.center.y = temp
          }
          if (fixDelta) {
            let temp = e.deltaX
            e.deltaX = -e.deltaY
            e.deltaY = temp
          }
        } else {
          //应该使用数学公式...
          console.log(`未知适配角度=${degree}`)
        }
      }

      /*
      触摸事件
      移动鼠标===
      .按下:[可能有press] panstart->doMouseMoveStart
      .移动:panmove->doMouseMove
      .抬起:panend->无操作

      按下并拖拽鼠标===
      .点击:press,tap-> doMousePress, setTimeout(doMouseUp)=tId
      .马上按下:press-> 取消tId, 无其他操作
      .移动:panmove->doMouseMove
      .抬起:panend->doMouseUp

      连续快速点击鼠标(双击,n击)===
      .首次点击:press,tap-> doMousePress, setTimeout(doMouseUp)=tId
      .马上点击:
          press-> 取消tId, 无其他操作
          tap->
              if(pressing) doMouseUp
              doMousePress, setTimeout(doMouseUp)=tId
      快速点击,并拖拽===
      .快速点击: 多个[press,tap]
      .按下移动抬起,或长按抬起: [press,panstart,panmove,panend], 或[press,pressup]
        按前面逻辑,无特殊处理
       */
      hammer.on("press", e => {
        // this.addLog(`${e.type}[${e.maxPointers}], [${e.center.x},${e.center.y}]`)
        if (e.target !== touchpad) return
        if (hammer.delayUpId) {
          clearTimeout(hammer.delayUpId)
          hammer.delayUpId = null
        }
      });
      hammer.on("tap", e => {
        // this.addLog(`${e.type}[${e.maxPointers}], [${e.center.x},${e.center.y}]`)
        if (e.target !== touchpad) return
        if (e.maxPointers === 1) {
          if (hammer.mousePressing) {
            hammer.doMouseUp(e)
          }
          hammer.mousePressing = true
          hammer.doMousePress(e)
          hammer.delayUpId = setTimeout(() => {
            hammer.delayUpId = null
            hammer.mousePressing = false
            hammer.doMouseUp(e)
          }, 200);
        } else if (e.maxPointers === 2) {
          hammer.doMouseButton(e, '4096', '右键')
        } else if (e.maxPointers === 3) {
          hammer.doMouseButton(e, '2048', '中键')
        }
      });
      hammer.on("pressup", e => {
        // this.addLog(`${e.type}[${e.maxPointers}], [${e.center.x},${e.center.y}]`)
        if (e.target !== touchpad) return
        hammer.doReset(e);
      });
      //移动事件
      hammer.on("panstart", e => {
        // this.addLog(`${e.type}[${e.maxPointers}], [${e.center.x},${e.center.y}]`)
        if (e.target !== touchpad) return
        if (e.maxPointers === 1) {
          hammer.fixRotate(e, true, false)
          hammer.doMouseMoveStart(e)
        } else if (e.maxPointers === 3) {
          // this.addLog(`reset, clickedMouseFrontBack, deltaTime=${e.deltaTime}`)
          if (e.deltaTime < 200) {
            /*
            重置[是否已点击了[前进,后退]]
            注意:要用deltaTime判断是[第一次panstart], 因为[多指]会导致[中途可有多个panstart]
            问题: clickedMouseFrontBack是为了避免[单次三指滑动]时,触发多次action,
              但实际上仍会'泄露', 因为要权衡[转屏后触摸板太小的情况]
             */
            hammer.clickedMouseFrontBack = false;
          }
        }
      });
      hammer.on("panmove", e => {
        // this.addLog(`${e.type}[${e.maxPointers}], [${e.center.x},${e.center.y}], [${e.deltaX},${e.deltaY}]`)
        if (e.target !== touchpad) return
        if (e.maxPointers === 1) {
          hammer.fixRotate(e, true, false)
          hammer.doMouseMove(e)
        } else if (e.maxPointers === 2) {
          //双指滑动, [滚动条事件]
          hammer.fixRotate(e, false, true)
          let deltaX = e.deltaX
          let deltaY = e.deltaY
          let ratio = this.touchRatio / 10
          let defRatio = 80//原始的[像素距离转换率]
          let scrollValueX = Math.round(deltaX / defRatio * ratio)
          let scrollValueY = Math.round(deltaY / defRatio * ratio)
          hammer.doScroll(e, false, scrollValueX)
          hammer.doScroll(e, true, scrollValueY)
        } else if (e.maxPointers === 3) {
          //三指左右滑动, [前进,后退]
          if (hammer.clickedMouseFrontBack) return
          hammer.fixRotate(e, false, true)
          let deltaX = e.deltaX
          let minDiff = 20
          // this.addLog(`minDiff=${minDiff}, deltaX=${deltaX}`)
          if (Math.abs(deltaX) > minDiff) {
            hammer.clickedMouseFrontBack = true
            hammer.doMouseButton(e,
              deltaX > 0 ? '32768' : '16384',
              deltaX > 0 ? '前进' : '后退'
            )
          }
        }
      });
      hammer.on("panend", e => {
        // this.addLog(`${e.type}[${e.maxPointers}], [${e.center.x},${e.center.y}]`)
        if (e.target !== touchpad) return
        hammer.doReset(e);
      });
      //场景: 三指操作触发系统了截屏功能
      hammer.on("pancancel", e => {
        this.addLog(`${e.type}[${e.maxPointers}], [${e.center.x},${e.center.y}]`)
        if (e.target !== touchpad) return
        hammer.doReset(e);
      });
    },
    addLog(str) {
      // console.log(str)
      if (!this.debug) return
      this.debugText = `${str} \n${this.debugText}`
    },
    //public方法.st=====
    /**
     * 设置按钮的baseSize[按钮,滚动条]
     * @param size 如"100px"
     * @param fontSize 如"1rem"
     */
    setBaseSize(size, fontSize) {
      // console.log('setBaseSize', size, fontSize)
      for (let button of this.$el.querySelectorAll('.touchpadButton button, .btnShowSetting')) {
        button.style.width = size
        button.style.height = size
        button.style.fontSize = fontSize
      }
    },
    /**
     * 全屏切换时,通知两个触摸板对象,更新配置
     */
    reloadConfig() {
      this.touchRatio = storageUtil.getObject('touch.ratio', 38);
      this.showMR = storageUtil.getObject('touch.showMR', false);
      this.showFB = storageUtil.getObject('touch.showFB', false);
    },
    //public方法.ed=====
  },
}
Vue.component('h-touchpad', component)